`include "mycpu.h"

module exe_stage(
    input                          clk           ,
    input                          reset         ,
    //allowin
    input                          ms_allowin    ,
    output                         es_allowin    ,
    //from ds
    input                          ds_to_es_valid,
    input  [`DS_TO_ES_BUS_WD -1:0] ds_to_es_bus  ,
    //from ws
    input  [`WS_TO_ES_BUS_WD -1:0] ws_to_es_bus  ,
    input                          ws_flush      ,
    input  [`MS_TO_ES_BUS_WD -1:0] ms_to_es_bus  ,
    input                          c0_status_exl ,
    //to ms
    output                         es_to_ms_valid,
    output [`ES_TO_MS_BUS_WD -1:0] es_to_ms_bus  ,
    //������ˮ��ͻ
    output [`ES_CONFLICT_BUS_WD -1:0] es_conflict_bus,
    // data sram interface
    output        data_sram_req  ,
    output        data_sram_wr   ,
    output [ 1:0] data_sram_size ,
    output [ 3:0] data_sram_wstrb,
    output [31:0] data_sram_addr ,
    output [31:0] data_sram_wdata,
    input         data_sram_addr_ok,
    //tlb search port 0
    output [18:0] vpn2,
    output        odd_page,
    input         found,
    input  [ 3:0] index,
    input  [19:0] pfn,
    input  [ 2:0] c,
    input         d,
    input         v
);

reg         es_valid      ;
wire        es_ready_go   ;
//DIV, DIVU blocked
wire        es_alu_div    ;
wire        es_alu_divu   ;
wire        op_div        ;
wire        div_finish    ;

reg  [`DS_TO_ES_BUS_WD -1:0] ds_to_es_bus_r;
wire [ 4:0] es_store_type;
wire [19:0] es_alu_op     ;
wire [ 6:0] es_load_type  ;
wire        es_src1_is_sa ;  
wire        es_src1_is_pc ;
wire        es_src2_is_imm;
wire        es_src2_is_immu;
wire        es_src2_is_8  ;
wire        es_gr_we      ;
wire        es_mem_we     ;
wire [ 4:0] es_dest       ;
wire [15:0] es_imm        ;
wire [31:0] es_rs_value   ;
wire [31:0] es_rt_value   ;
wire [31:0] es_pc         ;

//tlb
wire unmapped;
wire [18:0] entryhi_vpn2;
wire [31:0] tlbp_result;
wire ms_entryhi_we;
wire ws_entryhi_we;
wire blocked;

/* privileged instruction */
wire [7:0] es_c0_addr;
wire es_op_tlbp;
wire es_op_tlbwi;
wire es_op_tlbr;
wire es_op_mfc0;
wire es_op_mtc0;
wire eret_flush;
wire ds_ex;
wire es_ex;
wire [4:0] ds_excode;
wire [4:0] es_excode;
wire es_adel;
wire es_ades;
wire es_tlbl;
wire es_tlbs;
wire es_ov;
wire es_mod;
wire ov_possible;
wire bd;
wire es_flush;
wire ms_flush;
wire flush;
wire [31:0] es_final_result;
wire inst_retake;
wire ds_tlb_refill;
wire es_tlb_refill;

//data sram
wire load_store;
wire size_0;
wire size_1;
wire size_2;
wire [ 3:0] addr_select;
wire [31:0] visual_addr;
wire [31:0] final_addr;

//if ws_flush, maintain data request
reg        req_wait_valid;
reg        req_wait_wr;
reg [ 1:0] req_wait_size;
reg [ 3:0] req_wait_wstrb;
reg [31:0] req_wait_addr;
reg [31:0] req_wait_wdata;


assign {inst_retake    ,  //178:178
        es_op_tlbr     ,  //177:177
        es_op_tlbwi    ,  //176:176
        es_op_tlbp     ,  //175:175
        ov_possible    ,  //174:174
        bd             ,  //173:173
        ds_excode      ,  //172:168
        ds_ex          ,  //167:167
        eret_flush     ,  //166:166
        es_op_mfc0     ,  //165:165
        es_op_mtc0     ,  //164:164
        es_c0_addr     ,  //163:156
        es_store_type  ,  //155:151
        es_load_type   ,  //150:144
        es_alu_op      ,  //143:124
        es_src1_is_sa  ,  //123:123
        es_src1_is_pc  ,  //122:122
        es_src2_is_imm ,  //121:121
        es_src2_is_immu,  //120:120
        es_src2_is_8   ,  //119:119
        es_gr_we       ,  //118:118
        ds_tlb_refill  ,  //117:117
        es_dest        ,  //116:112
        es_imm         ,  //111:96
        es_rs_value    ,  //95 :64
        es_rt_value    ,  //63 :32
        es_pc             //31 :0
       } = ds_to_es_bus_r;
assign {ms_entryhi_we,
        ms_flush
       } = ms_to_es_bus;
assign {ws_entryhi_we,
        entryhi_vpn2
       } = ws_to_es_bus;

wire [31:0] es_alu_src1   ;
wire [31:0] es_alu_src2   ;
wire [31:0] es_alu_result ;
wire        es_alu_overflow; 

wire        es_res_from_mem;
wire [ 3:0] byte_wen;
wire [ 3:0] select;
wire [31:0] store_left;
wire [31:0] store_right;

assign es_res_from_mem = |es_load_type;
assign es_mem_we = |es_store_type;
assign es_to_ms_bus = {es_tlb_refill  ,  //133:133
                       inst_retake    ,  //132:132
                       es_op_tlbp     ,  //131:131
                       es_op_tlbr     ,  //130:130
                       es_op_tlbwi    ,  //129:129
                       data_sram_req  ,  //128:128
                       bd             ,  //127:127
                       es_excode      ,  //126:122
                       es_ex          ,  //121:121
                       eret_flush     ,  //120:120
                       es_op_mfc0     ,  //119:119
                       es_op_mtc0     ,  //118:118
                       es_c0_addr     ,  //117:110
                       es_rt_value    ,  //109:78
                       es_load_type   ,  //77:71
                       es_res_from_mem,  //70:70
                       es_gr_we       ,  //69:69
                       es_dest        ,  //68:64
                       es_final_result,  //63:32
                       es_pc             //31:0
                      };

assign op_div         = es_alu_op[14] | es_alu_op[15];
assign es_ready_go    = load_store? data_sram_req && data_sram_addr_ok && !req_wait_valid : !blocked;
assign es_allowin     = !es_valid || es_ready_go && ms_allowin;
assign es_to_ms_valid =  es_valid && es_ready_go;
always @(posedge clk) begin
    if (reset || ws_flush)
        es_valid <= 1'b0;
    else if (es_allowin)
        es_valid <= ds_to_es_valid;

    if (reset || ws_flush)
        ds_to_es_bus_r <= 179'd0;
    else if (ds_to_es_valid && es_allowin)
        ds_to_es_bus_r <= ds_to_es_bus;
end

assign es_alu_src1 = {32{es_src1_is_sa}} & {27'b0, es_imm[10:6]}
                    |{32{es_src1_is_pc}} & es_pc[31:0]
                    |{32{!es_src1_is_sa && !es_src1_is_pc}} & es_rs_value;
assign es_alu_src2 = {32{es_src2_is_imm}} & {{16{es_imm[15]}}, es_imm[15:0]}
                    |{32{es_src2_is_immu}} & {16'd0, es_imm[15:0]}
                    |{32{es_src2_is_8}} & 32'd8
                    |{32{!es_src2_is_imm && !es_src2_is_immu && !es_src2_is_8}} & es_rt_value;
assign es_alu_div  = ds_to_es_bus[138] && ds_to_es_valid && es_allowin;
assign es_alu_divu = ds_to_es_bus[139] && ds_to_es_valid && es_allowin;

alu u_alu(
    .alu_op     (es_alu_op    ),
    .alu_src1   (es_alu_src1  ),
    .alu_src2   (es_alu_src2  ),
    .clk        (clk          ),
    .reset      (reset        ),
    .alu_div    (es_alu_div   ),
    .alu_divu   (es_alu_divu  ),
    .flush      (flush        ),
    .alu_result (es_alu_result),
    .alu_overflow(es_alu_overflow),
    .div_finish (div_finish   )
    );
//������ˮ�߳�ͻ
wire  es_conflict_valid;
assign es_conflict_valid = es_gr_we & es_valid;
assign es_conflict_bus = {es_conflict_valid, es_dest};


wire es_inst_lb;
wire es_inst_lbu;
wire es_inst_lh;
wire es_inst_lhu;
wire es_inst_lw;
wire es_inst_lwl;
wire es_inst_lwr;
assign es_inst_lb  = es_load_type[0];
assign es_inst_lbu = es_load_type[1];
assign es_inst_lh  = es_load_type[2];
assign es_inst_lhu = es_load_type[3];
assign es_inst_lw  = es_load_type[4];
assign es_inst_lwl = es_load_type[5];
assign es_inst_lwr = es_load_type[6];


wire es_inst_sb;
wire es_inst_sh;
wire es_inst_sw;
wire es_inst_swl;
wire es_inst_swr;
assign es_inst_sb  = es_store_type[0];
assign es_inst_sh  = es_store_type[1];
assign es_inst_sw  = es_store_type[2];
assign es_inst_swl = es_store_type[3];
assign es_inst_swr = es_store_type[4];
assign select[0] = (es_alu_result[1:0] == 2'b00);
assign select[1] = (es_alu_result[1:0] == 2'b01);
assign select[2] = (es_alu_result[1:0] == 2'b10);
assign select[3] = (es_alu_result[1:0] == 2'b11);
assign addr_select = (es_inst_lwl || es_inst_swl)? 4'd1 : select;
assign byte_wen[0] = size_0 & addr_select[0] | size_1 & addr_select[0] | size_2 & addr_select[0];
assign byte_wen[1] = size_0 & addr_select[1] | size_1 & addr_select[0] | size_2;
assign byte_wen[2] = size_0 & addr_select[2] | size_1 & addr_select[2] | size_2;
assign byte_wen[3] = size_0 & addr_select[3] | size_1 & addr_select[2] | size_2 & !(es_inst_swl & select[2]);
assign store_left = {32{select[0]}} & {24'd0, es_rt_value[31:24]}
                  | {32{select[1]}} & {16'd0, es_rt_value[31:16]}
                  | {32{select[2]}} & { 8'd0, es_rt_value[31: 8]}
                  | {32{select[3]}} &         es_rt_value[31: 0];
assign store_right = {32{select[0]}} &  es_rt_value[31: 0]
                   | {32{select[1]}} & {es_rt_value[23: 0],  8'd0}
                   | {32{select[2]}} & {es_rt_value[15: 0], 16'd0}
                   | {32{select[3]}} & {es_rt_value[ 7: 0], 24'd0};

//data sram
assign size_0 = es_inst_lb | es_inst_lbu | es_inst_sb | (es_inst_lwl | es_inst_swl) & select[0] | (es_inst_lwr | es_inst_swr) & select[3];
assign size_1 = es_inst_lh | es_inst_lhu | es_inst_sh | (es_inst_lwl | es_inst_swl) & select[1] | (es_inst_lwr | es_inst_swr) & select[2];
assign size_2 = es_inst_lw | es_inst_sw | (es_inst_lwl | es_inst_swl) & (select[2] | select[3]) | (es_inst_lwr | es_inst_swr) & (select[0] | select[1]);
assign load_store = (es_res_from_mem || es_mem_we) && !es_ex && !es_flush;
assign visual_addr = req_wait_valid? req_wait_addr : (es_inst_lwl | es_inst_swl)? {es_alu_result[31:2], 2'd0} : es_alu_result;
assign data_sram_req   = ms_allowin && es_valid && load_store || req_wait_valid;
assign data_sram_wr    = req_wait_valid? req_wait_wr : |data_sram_wstrb;
assign data_sram_size  = req_wait_valid? req_wait_size : {2{size_0}} & 2'd0 | {2{size_1}} & 2'd1 | {2{size_2}} & 2'd2;
assign data_sram_wstrb = req_wait_valid? req_wait_wstrb : {4{es_mem_we}} & byte_wen[3:0];
assign data_sram_addr  = final_addr;
assign data_sram_wdata = req_wait_valid? req_wait_wdata : 
                    {32{es_inst_sb}} & {4{es_rt_value[ 7:0]}}
                  | {32{es_inst_sh}} & {2{es_rt_value[15:0]}}
                  | {32{es_inst_sw}} & es_rt_value
                  | {32{es_inst_swl}} & store_left
                  | {32{es_inst_swr}} & store_right;


/* privileged instruction */
assign es_adel = (es_inst_lh || es_inst_lhu) && (es_alu_result[0] == 1'b1)
               || es_inst_lw && !(es_alu_result[1:0] == 2'd0);
assign es_ades = es_inst_sh &&  (es_alu_result[0] == 1'b1)
              || es_inst_sw && !(es_alu_result[1:0] == 2'd0);
assign es_tlbl = es_res_from_mem && !unmapped && (!found || found && !v);
assign es_tlbs = es_mem_we && !unmapped && (!found || found && !v); 
assign es_ov = ov_possible && es_alu_overflow;
assign es_mod = es_mem_we && !unmapped && found && v && !d;
assign es_ex = ds_ex || es_adel || es_ades || es_ov || es_tlbl || es_tlbs || es_mod;
assign es_excode = ds_ex? ds_excode : 
                   es_ov? `EX_OV : 
                   (es_adel || es_ades)? {5{es_adel}} & `EX_ADEL | {5{es_ades}} & `EX_ADES :
                   {5{es_tlbl}} & `EX_TLBL | {5{es_tlbs}} & `EX_TLBS | {5{es_mod}} & `EX_MOD;
assign es_tlb_refill = ds_tlb_refill || (es_res_from_mem || es_mem_we) && !unmapped && !found;
assign es_final_result = (ds_ex && ((ds_excode == `EX_ADEL) || (ds_excode == `EX_TLBL)))? es_pc :
                         (es_tlbl || es_tlbs || es_mod)? visual_addr :
                         {32{es_op_mtc0}} & es_rt_value 
                       | {32{es_op_tlbp}} & tlbp_result 
                       | {32{!es_op_mtc0 && !es_op_tlbp}} & es_alu_result;
assign es_flush = inst_retake;
assign flush = es_flush | ms_flush | ws_flush;

always @(posedge clk) begin
    if(reset)
        req_wait_valid <= 1'b0;
    else if(ws_flush && data_sram_req && !data_sram_addr_ok)
        req_wait_valid <= 1'b1;
    else if(data_sram_addr_ok)
        req_wait_valid <= 1'b0;

    if(reset)
        req_wait_wr <= 1'b0;
    else if(ws_flush)
        req_wait_wr <= data_sram_wr;

    if(reset)
        req_wait_size <= 2'd0;
    else if(ws_flush)
        req_wait_size <= data_sram_size;
    
    if(reset)
        req_wait_wstrb <= 4'd0;
    else if(ws_flush)
        req_wait_wstrb <= data_sram_wstrb;

    if(reset)
        req_wait_addr <= 32'd0;
    else if(ws_flush)
        req_wait_addr <= data_sram_addr;

    if(reset)
        req_wait_wdata <= 32'd0;
    else if(ws_flush)
        req_wait_wdata <= data_sram_wdata;

end

//tlb
assign unmapped = visual_addr[31] & ~visual_addr[30];
assign vpn2 = {19{es_op_tlbp}} & entryhi_vpn2 | {19{!es_op_tlbp}} & visual_addr[31:13];
assign odd_page = visual_addr[12];
assign final_addr = {32{unmapped}} & {3'd0, visual_addr[28:0]} | {32{!unmapped}} & {pfn, visual_addr[11:0]};
assign tlbp_result = {!found, 27'd0, index};
assign blocked = es_op_tlbp && (ms_entryhi_we || ws_entryhi_we) || op_div && !div_finish;

endmodule
